package com.weifly.egp.service.stock;

import com.weifly.egp.service.enums.BuyOrSellEnum;
import com.weifly.egp.service.enums.OrderStatusEnum;
import com.weifly.egp.service.enums.SendCategory;
import com.weifly.egp.service.monitor.StockInfo;
import com.weifly.egp.service.trade.SendOrderInfo;
import com.weifly.egp.service.trade.StockOrderInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * Created by weijianxin on 2018/11/3.
 */
public class StockCheckAction {

    private Logger logger = LoggerFactory.getLogger(StockCheckAction.class);

    /**
     * 检查一只股票的信息
     * @param stockInfo
     * @param ctx
     */
    public void check(StockInfo stockInfo, StockUpdateContext ctx){
        // 获得股票价格
        StockPrice price = ctx.getStockPrice(stockInfo.getStockCode());
        if(price==null || price.getNowPrice()==null || price.getNowPrice()<=0){
            logger.warn("无法获得价格："+stockInfo.getStockCode());
        }else{
            // logger.info("获得股票价格，代码="+stockInfo.getStockCode()+", 最新价="+price.getNowPrice());
            this.updateStockPrice(stockInfo, price);
        }

        // 处理买入
        if(stockInfo.getBuyOrderInfo()==null){
            // 没有委托单号，查询委托情况
            this.findOrCreateBuyOrder(stockInfo, ctx);
        }else{
            // 有委托单号，检查是否成交
            this.checkBuyOrder(stockInfo, ctx);
        }

        // 处理卖出
        if(stockInfo.getSellOrderInfo()==null){
            // 没有委托单号，查询委托情况
            this.findOrCreateSellOrder(stockInfo, ctx);
        }else{
            // 有委托单号，检查是否成交
            this.checkSellOrder(stockInfo, ctx);
        }

        // 比对价格
        if(stockInfo.getBuyOrderInfo()!=null){
            if(!stockInfo.getBuyOrderInfo().getEntrustPrice().equals(stockInfo.takeBuyPrice())){
                // 买入委托价格 != 网格买入价，说明买入成交了
                stockInfo.setBuyOrderInfo(null);
                stockInfo.setBuyOrderUpdateTime(System.currentTimeMillis());
            }
        }
        if(stockInfo.getSellOrderInfo()!=null){
            if(!stockInfo.getSellOrderInfo().getEntrustPrice().equals(stockInfo.takeSellPrice())){
                // 卖出委托价格 != 网格卖出价，说明卖出成交了
                stockInfo.setSellOrderInfo(null);
                stockInfo.setSellOrderUpdateTime(System.currentTimeMillis());
            }
        }

        ctx.fireStockListChange();
    }

    // 更新股票价格信息
    private void updateStockPrice(StockInfo stockInfo, StockPrice price){
        stockInfo.setNewestPrice(price.getNowPrice());
        stockInfo.setNewestPriceUpdateTime(price.getUpdateTime());
        stockInfo.getPriceList().add(price);

        // 保留2分钟的价格信息
        long startTime = System.currentTimeMillis() - 120*1000;
        List<StockPrice> removedPriceList = new ArrayList<>();
        for(StockPrice priceInfo : stockInfo.getPriceList()){
            if(priceInfo.getUpdateTime()<startTime){
                removedPriceList.add(priceInfo);
            }else{
                break;
            }
        }
        for(StockPrice removedPrice : removedPriceList){
            stockInfo.getPriceList().remove(removedPrice);
        }
    }

    private void findOrCreateBuyOrder(StockInfo stockInfo, StockUpdateContext ctx){
        List<StockOrderInfo> orderList = ctx.getOrderList();
        Double targetPrice = stockInfo.takeBuyPrice();
        StockOrderInfo targetOrder = this.findOrder(orderList, stockInfo.getStockCode(), targetPrice, BuyOrSellEnum.buy);
        if(targetOrder==null){
            // 下单
            this.sendBuyOrder(stockInfo, ctx);
        }else{
            // 设置单
            stockInfo.setBuyOrderInfo(targetOrder);
            stockInfo.setBuyOrderUpdateTime(System.currentTimeMillis());
            this.printOperateInfo("关联买入单", stockInfo, targetOrder);
        }
    }

    private void checkBuyOrder(StockInfo stockInfo, StockUpdateContext ctx){
        boolean checkReady = (System.currentTimeMillis() - stockInfo.getBuyOrderUpdateTime()) > 30 *1000; // 买入委托单更新间隔大于30秒
        if(!checkReady){
            long buyOrderUpdateTime = stockInfo.getBuyOrderUpdateTime();
            double buyOrderPrice = stockInfo.getBuyOrderInfo().getEntrustPrice();
            for(StockPrice priceInfo : stockInfo.getPriceList()){
                if(priceInfo.getUpdateTime()>=buyOrderUpdateTime && priceInfo.getNowPrice().doubleValue()<=buyOrderPrice){
                    checkReady = true; // 上次更新时间 到 当前时间，价格曾经低于买入价格
                    break;
                }
            }
        }

        if(checkReady){
            List<StockOrderInfo> orderList = ctx.getOrderList();
            StockOrderInfo targetBuyOrder = this.findOrder(orderList, stockInfo.getBuyOrderInfo().getOrderNumber());
            if(targetBuyOrder==null){
                logger.info("没有查找到指定的委托单："+stockInfo.getBuyOrderInfo().getOrderNumber());
                return;
            }

            switch(targetBuyOrder.getOrderStatus()){
                case status_1: // 待撤
                case status_2: // 正撤
                case status_3: // 部撤
                case status_4: // 已撤
                    logger.info("买入委托单，已申请撤销，清除买入委托单 " + targetBuyOrder.getOrderNumber());
                    stockInfo.setBuyOrderInfo(null);
                    stockInfo.setBuyOrderUpdateTime(System.currentTimeMillis());
                    break;

                case status_5: // 未报
                case status_6: // 待报
                case status_7: // 正报
                case status_8: // 已报
                    logger.info("买入委托单，还没有成交 "+targetBuyOrder.getOrderNumber());
                    stockInfo.setBuyOrderInfo(targetBuyOrder);
                    stockInfo.setBuyOrderUpdateTime(System.currentTimeMillis());
                    break;

                case status_9: // 部成
                    logger.info("买入委托单，部分成交 "+targetBuyOrder.getOrderNumber());
                    stockInfo.setBuyOrderInfo(targetBuyOrder);
                    stockInfo.setBuyOrderUpdateTime(System.currentTimeMillis());
                    break;

                case status_10: // 已成
                    this.updateBuyOrder(stockInfo, ctx, targetBuyOrder);
                    break;

                case status_11: // 撤废
                    logger.info("买入委托单，撤废！等待下次检查 " + targetBuyOrder.getOrderNumber());
                    break;

                case status_12: // 废单
                    logger.info("买入委托单，废单！清除买入委托单 "+targetBuyOrder.getOrderNumber());
                    stockInfo.setBuyOrderInfo(null);
                    stockInfo.setBuyOrderUpdateTime(System.currentTimeMillis());
                    break;
            }
        }
    }

    /**
     * 买入委托单成交，更新各种信息
     *
     * @param stockInfo
     * @param ctx
     * @param buyOrder
     */
    private void updateBuyOrder(StockInfo stockInfo, StockUpdateContext ctx, StockOrderInfo buyOrder){
        this.printOperateInfo("已成交", stockInfo, buyOrder);

        // 更新基准价
        Double newBasePrice = stockInfo.takeBuyPrice();
        stockInfo.setBasePrice(newBasePrice);

        // 写回TradeInfo.xml
        ctx.saveConfig();

        // 清除买入委托单
        stockInfo.setBuyOrderInfo(null);
        stockInfo.setBuyOrderUpdateTime(System.currentTimeMillis());

        // 下单或关联已有单
        this.findOrCreateBuyOrder(stockInfo, ctx);
    }

    // 买入委托单，下单
    private void sendBuyOrder(StockInfo stockInfo, StockUpdateContext ctx){
        Double targetPrice = stockInfo.takeBuyPrice();
        SendOrderInfo sendOrder = this.createSendOrder(stockInfo, BuyOrSellEnum.buy, targetPrice);
        StockOrderInfo targetOrder = ctx.sendOrder(sendOrder);
        if(targetOrder==null){
            logger.info("买入下单失败，请查找原因！"+stockInfo.getStockCode());
        }else{
            stockInfo.setBuyOrderInfo(targetOrder);
            stockInfo.setBuyOrderUpdateTime(System.currentTimeMillis());
            this.printOperateInfo("下单买入", stockInfo, targetOrder);
        }
    }

    private void printOperateInfo(String msg, StockInfo stockInfo, StockOrderInfo orderInfo){
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        StringBuilder sb = new StringBuilder();
        sb.append(msg);
        sb.append(", 委托时间：").append(sdf.format(new Date(orderInfo.getOrderTime())));
        sb.append(", 申请编号: ").append(orderInfo.getOrderNumber());
        sb.append(", 证券代码: ").append(orderInfo.getStockCode());
        sb.append(", 证券名称: ").append(orderInfo.getStockName());
        sb.append(", 委托方向: ").append(orderInfo.getBuyOrSell().getName());
        sb.append(", 委托状态: ").append(orderInfo.getOrderStatus().getStatus());
        sb.append(", 委托价格：").append(orderInfo.getEntrustPrice());
        sb.append(", 委托数量: ").append(orderInfo.getEntrustSize());
        logger.info(sb.toString());
    }

    // 查找指定价格、未成交的订单
    private StockOrderInfo findOrder(List<StockOrderInfo> orderList, String stockCode, Double targetPrice, BuyOrSellEnum buyEnum){
        for(StockOrderInfo order : orderList){
            boolean validOrder = (order.getOrderStatus()!=null) && ((order.getOrderStatus().equals(OrderStatusEnum.status_5)
                    || order.getOrderStatus().equals(OrderStatusEnum.status_6)
                    || order.getOrderStatus().equals(OrderStatusEnum.status_7)
                    || order.getOrderStatus().equals(OrderStatusEnum.status_8)));
            if(order.getStockCode().equals(stockCode) // 指定股票
                    && order.getEntrustPrice().equals(targetPrice) // 指定价格
                    && order.getBuyOrSell().equals(buyEnum) // 买或卖
                    && validOrder){ // 未成交
                return order;
            }
        }
        return null;
    }

    /**
     * 根据委托单号，查找委托单
     *
     * @param orderList
     * @param orderNumber
     * @return
     */
    private StockOrderInfo findOrder(List<StockOrderInfo> orderList, String orderNumber){
        for(StockOrderInfo order : orderList){
            if(orderNumber.equals(order.getOrderNumber())){
                return order;
            }
        }
        return null;
    }

    /**
     * 创建委托单
     *
     * @param stockInfo
     * @param buyOrSell
     * @param entrustPrice 委托价格
     * @return
     */
    private SendOrderInfo createSendOrder(StockInfo stockInfo, BuyOrSellEnum buyOrSell, Double entrustPrice){
        SendOrderInfo sendOrder = new SendOrderInfo();

        if(buyOrSell.equals(BuyOrSellEnum.buy)){
            if(stockInfo.isCreditBuy()){
                sendOrder.setCategory(SendCategory.category_2); // 融资买入
            }else{
                sendOrder.setCategory(SendCategory.category_0); // 担保品买入
            }
        }else if(buyOrSell.equals(BuyOrSellEnum.sell)){
            if(stockInfo.isCreditBuy()){
                sendOrder.setCategory(SendCategory.category_1); // 卖出
            }else{
                sendOrder.setCategory(SendCategory.category_1); // 卖出
            }
        }else{
            throw new RuntimeException("不支持的类型 "+buyOrSell);
        }

        sendOrder.setStockCode(stockInfo.getStockCode());
        sendOrder.setEntrustPrice(entrustPrice);
        sendOrder.setEntrustSize(stockInfo.getTradeUnit());

        sendOrder.setStockName(stockInfo.getStockName());
        sendOrder.setBuyOrSell(buyOrSell);
        sendOrder.setCreditBuy(stockInfo.isCreditBuy());

        return sendOrder;
    }

    private void findOrCreateSellOrder(StockInfo stockInfo, StockUpdateContext ctx){
        List<StockOrderInfo> orderList = ctx.getOrderList();
        Double targetPrice = stockInfo.takeSellPrice();
        StockOrderInfo targetOrder = this.findOrder(orderList, stockInfo.getStockCode(), targetPrice, BuyOrSellEnum.sell);
        if(targetOrder==null){
            // 下单
            this.sendSellOrder(stockInfo, ctx);
        }else{
            // 设置单
            stockInfo.setSellOrderInfo(targetOrder);
            stockInfo.setSellOrderUpdateTime(System.currentTimeMillis());
            this.printOperateInfo("关联卖出单", stockInfo, targetOrder);
        }
    }

    private void checkSellOrder(StockInfo stockInfo, StockUpdateContext ctx){
        boolean checkReady = (System.currentTimeMillis() - stockInfo.getSellOrderUpdateTime()) > 30 *1000; // 卖出委托单更新间隔大于30秒
        if(!checkReady){
            long sellOrderUpdateTime = stockInfo.getSellOrderUpdateTime();
            double sellOrderPrice = stockInfo.getSellOrderInfo().getEntrustPrice();
            for(StockPrice priceInfo : stockInfo.getPriceList()){
                if(priceInfo.getUpdateTime()>=sellOrderUpdateTime && priceInfo.getNowPrice().doubleValue()>=sellOrderPrice){
                    checkReady = true; // 上次更新时间 到 当前时间，价格曾经大于卖出价格
                    break;
                }
            }
        }

        if(checkReady){
            List<StockOrderInfo> orderList = ctx.getOrderList();
            StockOrderInfo targetSellOrder = this.findOrder(orderList, stockInfo.getSellOrderInfo().getOrderNumber());
            if(targetSellOrder==null){
                logger.info("没有查找到指定的卖出委托单："+stockInfo.getSellOrderInfo().getOrderNumber());
                return;
            }

            switch(targetSellOrder.getOrderStatus()){
                case status_1: // 待撤
                case status_2: // 正撤
                case status_3: // 部撤
                case status_4: // 已撤
                    logger.info("卖出委托单，已申请撤销，清除卖出委托单 " + targetSellOrder.getOrderNumber());
                    stockInfo.setSellOrderInfo(null);
                    stockInfo.setSellOrderUpdateTime(System.currentTimeMillis());
                    break;

                case status_5: // 未报
                case status_6: // 待报
                case status_7: // 正报
                case status_8: // 已报
                    logger.info("卖出委托单，还没有成交 "+targetSellOrder.getOrderNumber());
                    stockInfo.setSellOrderInfo(targetSellOrder);
                    stockInfo.setSellOrderUpdateTime(System.currentTimeMillis());
                    break;

                case status_9: // 部成
                    logger.info("卖出委托单，部分成交 "+targetSellOrder.getOrderNumber());
                    stockInfo.setSellOrderInfo(targetSellOrder);
                    stockInfo.setSellOrderUpdateTime(System.currentTimeMillis());
                    break;

                case status_10: // 已成
                    this.updateSellOrder(stockInfo, ctx, targetSellOrder);
                    break;

                case status_11: // 撤废
                    logger.info("卖出委托单，撤废！等待下次检查 " + targetSellOrder.getOrderNumber());
                    break;

                case status_12: // 废单
                    logger.info("卖出委托单，废单！清除卖出委托单 "+targetSellOrder.getOrderNumber());
                    stockInfo.setSellOrderInfo(null);
                    stockInfo.setSellOrderUpdateTime(System.currentTimeMillis());
                    break;
            }
        }
    }

    // 卖出委托单，下单
    private void sendSellOrder(StockInfo stockInfo, StockUpdateContext ctx){
        Double targetPrice = stockInfo.takeSellPrice();
        SendOrderInfo sendOrder = this.createSendOrder(stockInfo, BuyOrSellEnum.sell, targetPrice);
        StockOrderInfo targetOrder = ctx.sendOrder(sendOrder);
        if(targetOrder==null){
            logger.info("卖出下单失败，请查找原因！"+stockInfo.getStockCode());
        }else{
            stockInfo.setSellOrderInfo(targetOrder);
            stockInfo.setSellOrderUpdateTime(System.currentTimeMillis());
            this.printOperateInfo("下单卖出", stockInfo, targetOrder);
        }
    }

    /**
     * 卖出委托单成交，更新各种信息
     *
     * @param stockInfo
     * @param ctx
     * @param sellOrder
     */
    private void updateSellOrder(StockInfo stockInfo, StockUpdateContext ctx, StockOrderInfo sellOrder){
        this.printOperateInfo("已成交", stockInfo, sellOrder);

        // 更新基准价
        Double newBasePrice = stockInfo.takeSellPrice();
        stockInfo.setBasePrice(newBasePrice);

        // 写回TradeInfo.xml
        ctx.saveConfig();

        // 清除卖出委托单
        stockInfo.setSellOrderInfo(null);
        stockInfo.setSellOrderUpdateTime(System.currentTimeMillis());

        // 下单或关联已有单
        this.findOrCreateSellOrder(stockInfo, ctx);
    }
}
